package com.iwdnb.gkgz.application.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.iwdnb.bmnf.common.service.CacheService;
import com.iwdnb.gkgz.application.service.StockFenshiService;
import com.iwdnb.gkgz.common.model.dto.Stock;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StockFenshiData;
import com.iwdnb.gkgz.common.quota.FenshiAverage;
import com.iwdnb.gkgz.common.quota.MacdIndicator;
import com.iwdnb.gkgz.common.quota.VolumeRatioIndicator;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;
import com.iwdnb.gkgz.common.utils.StockBlackListUtils;
import com.iwdnb.gkgz.common.utils.StockFenshiUtils;
import com.iwdnb.gkgz.common.utils.StockUtils;
import com.iwdnb.gkgz.common.utils.WorkDayUtils;
import com.iwdnb.gkgz.infrastructure.dao.client.MairuiClient;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockDayDataRepository;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockFenshiRepository;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockRepository;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class StockFenshiServiceImpl implements StockFenshiService {
    @Autowired
    private StockDayDataRepository stockDayDataRepository;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private StockFenshiRepository stockFenshiRepository;
    @Autowired
    private Executor executor;
    @Autowired
    private MairuiClient mairuiClient;
    @Autowired
    private StockRepository stockRepository;

    private static final List<String> sellTypes = Lists.newArrayList("getRiseUpData", "getDivergeData",
        "getAveragePressData", "getAverageReverseLtOpenPriceData");
    //private static final List<String> sellTypes = Lists.newArrayList(
    //    "getAveragePressData");

    @Override
    public Map<String, FenshiStatistics> calculateSellInfo(String date, String codeStr) {
        List<Stock> stockList = stockRepository.findAll();
        List<String> codes = stockList.stream()
            .map(stock -> stock.getCode()).collect(Collectors.toList());
        codes = StockBlackListUtils.getCanBuyCodeList(codes);
        if (StringUtils.isNotBlank(codeStr)) {
            codes = Lists.newArrayList(codeStr.split(","));
        }
        Map<String, FenshiStatistics> statisticsMap = initStatisticsMap();
        CountDownLatch latch = new CountDownLatch(codes.size());
        for (String code : codes) {
            executor.execute(() -> {
                StockFenshiData fenshiData = stockFenshiRepository.queryFenshiList(code, date);
                if (fenshiData == null) {
                    latch.countDown();
                    return;
                }
                List<StockDayData> fenshiList = fenshiData.getFenshiList();
                if (CollectionUtils.isEmpty(fenshiList) || fenshiList.size() <= 1) {
                    latch.countDown();
                    return;
                }
                MacdIndicator.calculateMACD(fenshiList, 4);
                VolumeRatioIndicator.calculateVolumeRatio(fenshiList, 4);
                FenshiAverage.calculateFenshiAverage(fenshiList);
                for (String sellType : sellTypes) {
                    //根据卖出类型计算卖出信息
                    StockDayData fenshiDayData = getSellInfo(fenshiList, sellType);
                    if (Objects.nonNull(fenshiDayData)) {
                        FenshiStatistics statistics = statisticsMap.get(sellType);
                        addFenshiStaticticData(statistics, fenshiDayData, fenshiList.get(fenshiList.size() - 1));
                    }
                }
                latch.countDown();
            });

        }
        try {
            latch.await(30, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (String sellType : sellTypes) {
            FenshiStatistics statistics = statisticsMap.get(sellType);
            log.info("{}-{}-卖出总次数:{},总收益:{}", date, statistics.getSellType(), statistics.getTotalCount(),
                statistics.getTotalRate());
        }
        return statisticsMap;
    }

    @Override
    public void fixFenshiBeforeClosePrice(String beginDate, String endDate) {
        List<Stock> stockList = stockRepository.findAll();
        List<String> codes = stockList.stream()
            .map(stock -> stock.getCode()).collect(Collectors.toList());
        codes = StockBlackListUtils.getCanBuyCodeList(codes);
        Date begin = DateUtil.parseDate(beginDate);
        Date end = DateUtil.parseDate(endDate);
        List<Date> dateList = WorkDayUtils.getWorkDayList(begin, end);
        CountDownLatch latch = new CountDownLatch(codes.size());
        for (String code : codes) {
            executor.execute(() -> {
                List<StockDayData> stockDayDataList = stockDayDataRepository.queryAll(code);
                if (CollectionUtils.isEmpty(stockDayDataList)) {
                    latch.countDown();
                    return;
                }
                for (Date date : dateList) {
                    StockFenshiData fenshiData = stockFenshiRepository.queryFenshiList(code, DateUtil.formatDate(date));
                    if (fenshiData == null) {
                        continue;
                    }
                    List<StockDayData> fenshiList = fenshiData.getFenshiList();
                    if (CollectionUtils.isEmpty(fenshiList) || fenshiList.size() <= 1) {
                        continue;
                    }
                    String beforeDate = DateUtil.formatDate(WorkDayUtils.offsetWorkDay(date, -1));
                    StockDayData data = StockUtils.getStockDayData(stockDayDataList, beforeDate);
                    if (Objects.nonNull(data)) {
                        fenshiList.get(0).setBeforeClosePrice(data.getClosePrice());
                        fenshiData.setFenshiList(fenshiList);
                        stockFenshiRepository.save(fenshiData);
                    }
                }
                latch.countDown();
            });

        }
        try {
            latch.await(30, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private StockDayData getSellInfo(List<StockDayData> fenshiList, String sellType) {
        if (CollectionUtils.isEmpty(fenshiList)) {
            return null;
        }
        StockDayData openData = fenshiList.get(0);
        BigDecimal beforeClosePrice = openData.getBeforeClosePrice();
        if (Objects.isNull(beforeClosePrice)) {
            beforeClosePrice = openData.getOpenPrice();
        }
        BigDecimal openRate = BigDecimalUtils.subStractAndDividePrecent(openData.getOpenPrice(), beforeClosePrice);
        switch (sellType) {
            case "getRiseUpData":
                return StockFenshiUtils.getRiseUpData(fenshiList, beforeClosePrice);
            case "getDivergeData":
                return StockFenshiUtils.getDivergeData(fenshiList, beforeClosePrice);
            case "getAveragePressData":
                return StockFenshiUtils.getAveragePressData(fenshiList, beforeClosePrice);
            case "getAverageReverseLtOpenPriceData":
                return StockFenshiUtils.getAverageReverseLtOpenPriceData(fenshiList, beforeClosePrice);
            default:
                return null;
        }
    }

    private synchronized void addFenshiStaticticData(FenshiStatistics statistics, StockDayData fenshiData, StockDayData
        closeData) {
        BigDecimal rate = BigDecimalUtils.subStractAndDividePrecent(fenshiData.getClosePrice(),
            closeData.getClosePrice());
        log.info("{}-{}-{}-卖出价:{},卖出后比较收益:{}", closeData.getCode(), statistics.getSellType(),
            fenshiData.getDatetime(),
            fenshiData.getClosePrice(), rate);
        statistics.setTotalCount(statistics.getTotalCount() + 1);
        statistics.setTotalRate(BigDecimalUtils.add(statistics.getTotalRate(), rate));
        if (BigDecimalUtils.isGt(rate, BigDecimal.ZERO)) {
            statistics.setSuccessCount(statistics.getSuccessCount() + 1);
            statistics.setSuccessRate(BigDecimalUtils.add(statistics.getSuccessRate(), rate));
        } else if (BigDecimalUtils.isLt(rate, BigDecimal.ZERO)) {
            statistics.setFailCount(statistics.getFailCount() + 1);
            statistics.setFailRate(BigDecimalUtils.add(statistics.getFailRate(), rate));
        }
    }

    private Map<String, FenshiStatistics> initStatisticsMap() {
        Map<String, FenshiStatistics> map = new HashMap<>();
        for (String sellType : sellTypes) {
            map.put(sellType,
                new FenshiStatistics(sellType, 0, BigDecimal.ZERO, 0,
                    BigDecimal.ZERO, 0, BigDecimal.ZERO));
        }

        return map;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class FenshiStatistics {

        private String sellType;

        private int totalCount;

        private BigDecimal totalRate;

        private int successCount;

        private BigDecimal successRate;

        private int failCount;

        private BigDecimal failRate;
    }

}
